﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;

namespace Microsoft.Goldenlight.Collections
{
    /// <summary>
    /// 	Represents a node in a QuadTree.
    /// </summary>
    /// <typeparam name="T"> Specifies the element type of the tree. </typeparam>
    [ComVisible(false)]
    public sealed class QuadTreeNode<T>
    {
        #region Static Operations

        private static QuadTreeNode<T> s_leaf;

        #region Allocate

        /// <summary>
        /// 	Allocates a instance of the QuadTreeNode class, containing the default value.
        /// </summary>
        /// <returns> A new instance of the QuadTreeNode class. </returns>
        public static QuadTreeNode<T> Allocate()
        {
            return Allocate(default(T));
        }

        /// <summary>
        /// 	Allocates a instance of the QuadTreeNode class, containing the specified value.
        /// </summary>
        /// <param name="value"> The value to contain in the QuadTreeNode. </param>
        /// <returns> A new instance of the QuadTreeNode class. </returns>
        public static QuadTreeNode<T> Allocate(T value)
        {
            if (s_leaf != null)
            {
                var allocatedNode = s_leaf;
                s_leaf = s_leaf.Parent;
                allocatedNode.Value = value;
                allocatedNode.Parent = null;
                return allocatedNode;
            }
            return new QuadTreeNode<T>(value);
        }

        /// <summary>
        /// 	Allocates a instance of the QuadTreeNode class, containing the specified value.
        /// </summary>
        /// <param name="tree"> A reference to the QuadTree that the QuadTreeNode belongs to, or null if the QuadTreeNode is not linked. </param>
        /// <param name="value"> The value to contain in the QuadTreeNode. </param>
        /// <returns> A new instance of the QuadTreeNode class. </returns>
        public static QuadTreeNode<T> Allocate(QuadTree<T> tree, T value)
        {
            if (s_leaf != null)
            {
                var allocatedNode = s_leaf;
                s_leaf = s_leaf.Parent;
                allocatedNode.Tree = tree;
                allocatedNode.Value = value;
                allocatedNode.Parent = null;
                return allocatedNode;
            }
            return new QuadTreeNode<T>(tree, value);
        }

        #endregion

        #region Free

        /// <summary>
        /// 	Recycle the instance of the QuadTreeNode class, containing the specified value.
        /// </summary>
        /// <param name="node"> The QuadTreeNode which to free. </param>
        public static void Free(QuadTreeNode<T> node)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }
            node.Tree = null;
            node.Value = default(T);
            node.Parent = s_leaf;
            node.Depth = 0;
            node.Bounds = default(BoundingRectangle);
            Array.Clear(node._children, 0, 4);
            s_leaf = node;
        }

        #endregion

        #endregion

        #region Fields & Properties

        /// <summary>
        /// 	The value contained in the node.
        /// </summary>
        public T Value;

        /// <summary>
        /// 	A reference to the QuadTree that the QuadTreeNode belongs to, or null if the QuadTreeNode is not added.
        /// </summary>
        public QuadTree<T> Tree;

        /// <summary>
        /// 	A reference to the parent node in the QuadTree, or null if the current node is the root of the QuadTree.
        /// </summary>
        public QuadTreeNode<T> Parent;

        /// <summary>
        /// 	The depth of this QuadTree node.
        /// </summary>
        public int Depth;

        /// <summary>
        /// 	Gets the bounds of the QuadTree node.
        /// </summary>
        public BoundingRectangle Bounds;

        #endregion

        #region Children Fields & Properties

        private readonly QuadTreeNode<T>[] _children = new QuadTreeNode<T>[4];

        /// <summary>
        /// 	Gets a read-only collection of the 4 child nodes.
        /// </summary>
        public IList<QuadTreeNode<T>> Children
        {
            get
            {
                return _children;
            }
        }

        /// <summary>
        /// 	Gets a value indicating whether the control contains child nodes.
        /// </summary>
        public bool HasChildren
        {
            get
            {
                return _children[0] != null;
            }
        }

        /// <summary>
        /// 	Gets or sets the node at the specified index.
        /// </summary>
        /// <param name="index"> The zero-based index of the node to get or set. </param>
        /// <returns> The node at the specified index. </returns>
        public QuadTreeNode<T> this[int index]
        {
            get
            {
                if (index >= 4)
                {
                    throw new ArgumentOutOfRangeException("index");
                }
                return _children[index];
            }
            set
            {
                if (index >= 4)
                {
                    throw new ArgumentOutOfRangeException("index");
                }
                _children[index] = value;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// 	Initializes a new instance of the QuadTreeNode class, containing the specified value.
        /// </summary>
        /// <param name="value"> The value to contain in the QuadTreeNode. </param>
        public QuadTreeNode(T value)
        {
            Value = value;
        }

        /// <summary>
        /// 	Initializes a new instance of the QuadTreeNode class, containing the specified value.
        /// </summary>
        /// <param name="tree"> A reference to the QuadTree that the QuadTreeNode belongs to, or null if the QuadTreeNode is not linked. </param>
        /// <param name="value"> The value to contain in the QuadTreeNode. </param>
        public QuadTreeNode(QuadTree<T> tree, T value)
        {
            Tree = tree;
            Value = value;
        }

        #endregion
    }
}